﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Xml.Linq;
using Phoenix.Xna.Framework.Properties;
using Phoenix.Xna.Framework.Reflection;
using Phoenix.Xna.Framework.Serialization.Markup.Objects;
using Phoenix.Xna.Framework.Xml;

namespace Phoenix.Xna.Framework.Serialization.Markup.Properties
{
    /// <summary>
    /// The intermediary markup property object for IList properties.
    /// </summary>
    internal class ListMarkupProperty : CollectionMarkupProperty
    {
        private List<IMarkupObject> _items = new List<IMarkupObject>();     // The items in the collection.

        /// <summary>
        /// Initializes a new instance of the ListMarkupProperty class.
        /// </summary>
        public ListMarkupProperty()
            : base()
        {
        }

        #region IMarkupElement Members

        /// <summary>
        /// Serialize the element to XML.
        /// </summary>
        /// <returns>The element markup serialzed as an XElement.</returns>
        public override XElement Serialize()
        {
            XElement element = base.Serialize();

            foreach (IMarkupObject child in _items)
            {
                XElement item = child.Serialize();
                item.SetAttribute(AttributeIDs.IsCollectionItemAttribute, true);
                element.Add(item);
            }

            return element;
        }

        /// <summary>
        /// Deserialize the element from an XElement.
        /// </summary>
        /// <param name="element">The XElement to deserialize this element from.</param>
        public override void Deserialize(XElement element)
        {
            base.Deserialize(element);

            foreach (XElement xmlProperty in element.Nodes())
            {
                if (xmlProperty.ReadAttribute<bool>(AttributeIDs.IsCollectionItemAttribute))
                {
                    IMarkupObject markupObject = Serializer.MarkupSelector.GetMarkupObjectFor(xmlProperty);
                    _items.Add(markupObject);
                    markupObject.Deserialize(xmlProperty);
                }
            }
        }

        #endregion

        #region IMarkupProperty Members

        /// <summary>
        /// Extract the markup information from the PropertyInfo for this property.
        /// </summary>
        /// <param name="field">The PropertyInfo for this property.</param>
        /// <param name="value">The value of the property.</param>
        public override void ExtractMarkup(PropertyInfo property, object value)
        {
            base.ExtractMarkup(property, value);

            if (value != null && property.GetFirstAttributeOfType<SerializableIListAttribute>(false) != null)
            {
                if (!typeof(IList).IsAssignableFrom(value.GetType()))
                {
                    throw new InvalidCastException(string.Format(CultureInfo.InvariantCulture, Resources.InvalidCastException, value.GetType().FullName, typeof(IList).Name));
                }

                IList list = value as IList;
                ShouldPersist = list.Count > 0;

                foreach (object child in list)
                {
                    IMarkupObject markupObject = Serializer.MarkupSelector.GetMarkupObjectFor(child);
                    _items.Add(markupObject);
                }                
            }
        }

        /// <summary>
        /// Sets the value of MarkupProperty to the instance's property value.
        /// </summary>
        /// <param name="instance">The instance to set the property on.</param>
        public override void SetValue(object instance)
        {
            base.SetValue(instance);
            IList list = (IList)_value;

            foreach (IMarkupObject child in _items)
            {
                if (child.Instance == null)
                {
                    child.ConstructInstance();
                }

                list.Add(child.Instance);
            }
        }

        #endregion       
    }
}
